package com.yh.csx.bsf.flow.engine.state;

import com.google.common.collect.Lists;
import com.yh.csx.bsf.core.base.Ref;
import com.yh.csx.bsf.core.thread.ThreadPool;
import com.yh.csx.bsf.core.util.ContextUtils;
import com.yh.csx.bsf.core.util.StringUtils;
import com.yh.csx.bsf.flow.core.FlowProperties;
import com.yh.csx.bsf.flow.provider.enums.TaskStateEnum;
import com.yh.csx.bsf.flow.core.base.FlowException;
import com.yh.csx.bsf.flow.core.base.Model;
import com.yh.csx.bsf.flow.core.base.Variable;
import com.yh.csx.bsf.flow.core.db.ConvertUtil;
import com.yh.csx.bsf.flow.core.db.DbHelper;
import com.yh.csx.bsf.flow.dao.dal.t_task_dal;
import com.yh.csx.bsf.flow.dao.model.auto.t_approve_log_model;
import com.yh.csx.bsf.flow.dao.model.auto.t_task_model;
import com.yh.csx.bsf.flow.engine.api.CommonApi;
import com.yh.csx.bsf.flow.engine.api.UserApi;
import lombok.val;
import lombok.var;
import org.apache.commons.lang.time.DateUtils;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @author: chejiangyi
 * @version: 2019-10-28 18:40
 **/
public class ManNode extends BaseNode{
    @Override
    public String getName(){
        return this.modelNode.getTitle();
    }
    @Override
    public void flow(int flowid){
        try {
            logFlowDebug(flowid,"节点:"+this.modelNode.getTitle()+" 进入flow");
            beginScript(flowid);
            var usernos = UserApi.getUserNos(flowid,0,this.flowVariables,this.modelNode.getApproveUser().getChooseUsers());
            usernos = getApprovalUserNos(usernos);

            //没有用户任务
            if (usernos.size() == 0) {
                logFlowDebug(flowid,"节点:"+this.modelNode.getTitle()+" 分配用户工号集合为空");
                if (route(flowid,0) == false) {
                    throw new StateEngineException(flowid,"未获取到用户进行任务分配");
                }
            } else {
                val flow = getFlowModel(flowid);
                long batch_id = ConvertUtil.strToInt64(new SimpleDateFormat("yyMMddHHmmssSSS").format(new Date()));
                if (this.modelNode.getApproveUser().getDistributeModeEnum() == Model.DistributeModeEnum.Once) {
                    logFlowDebug(flowid,"节点:"+this.modelNode.getTitle()+" 一次性分配用户:"+String.join(",",usernos));
                    for (val userno : usernos) {
                        val user = UserApi.getUser(userno);
                        //创建用户任务
                        val model = new t_task_model(0, flowid, flow.title, this.modelNode.getTitle(), user.getNo()+"", user.getName(), "", (byte) TaskStateEnum.waiting.Value,
                                new Date(), "系统", new Date(), "系统",nextTimeout(0),nextTimeoutWithTime(new Date(),0),"",batch_id);
                        val taskid = DbHelper.get((c) -> new t_task_dal().add2(c, model));
                        sendMessage(taskid,this.modelNode.getStartNotify(), userno, model.flow_title, ContextUtils.getBean(FlowProperties.class, false).getStartNotifyTemplate());
                    }
                } else if (this.modelNode.getApproveUser().getDistributeModeEnum() == Model.DistributeModeEnum.OneByOne) {
                    logFlowDebug(flowid,"节点:"+this.modelNode.getTitle()+" 一个个逐步分配用户:"+String.join(",",usernos));
                    //创建用户任务
                    val firstuser = usernos.get(0);
                    usernos.remove(0);
                    val userjoin = String.join(",", usernos);
                    val user = UserApi.getUser(firstuser);
                    val model = new t_task_model(0, flowid, flow.title, this.modelNode.getTitle(), user.getNo(), user.getName(), userjoin, (byte) TaskStateEnum.waiting.Value,
                            new Date(), "系统", new Date(), "系统",nextTimeout(0),nextTimeoutWithTime(new Date(),0),"",batch_id);
                    val taskid = DbHelper.get((c) -> new t_task_dal().add2(c, model));
                    sendMessage(taskid,this.modelNode.getStartNotify(), firstuser, model.flow_title,ContextUtils.getBean(FlowProperties.class, false).getStartNotifyTemplate());
                }
                refreshFlowApprove(flowid);
            }
        }catch (Exception e){
            throw filterException(flowid,e);
        }
    }

    private void sendMessage(int taskid,Model.NotifyEnum notfiyEnum, String userno,String subject, final String msg){
        val task = getTaskModel(taskid);
        ThreadPool.System.submit("流程消息异步发送",()->{
            int flowid = -1;
            try {
                flowid = task.flow_id;
                val msg2 = msg.replace("@tasktitle",task.getFlow_title()).replace("@taskurl",
                        ContextUtils.getBean(FlowProperties.class,false).getTaskUrlTemplate().replace("@taskid",taskid+""))
                        .replace("@flowid",task.flow_id+"");
                if (notfiyEnum == Model.NotifyEnum.SMS) {
                    CommonApi.sms(flowid,userno,msg2);
                } else if (notfiyEnum == Model.NotifyEnum.Mail) {
                    CommonApi.mail(flowid,userno,"【办公流程】"+subject,msg2);
                }
            }catch (Exception e){
                val e2 = new FlowException("节点:"+this.modelNode.getTitle()+" 消息发送出错",e);
                onlyLogError(flowid,e2);
            }
        });
    }

    public void triggerTimeout(int taskid){
        int flowid = -1;
        try {
            val task = getTaskModel(taskid);flowid = task.flow_id;
            //if(task.timeout_time.getTime()>new Date().getTime()) {
            Ref<Model.TimeOutRule> currentRule = new Ref<>(null);
            Ref<Model.TimeOutRule> nextRule = new Ref<>(null);
            parseNextTimeout(task.timeout, currentRule, nextRule);
            if (currentRule != null) {
                doTimeoutRule(task, currentRule.getData());
                if (nextRule.getData() != null) {
                    DbHelper.call((c) -> {
                        new t_task_dal().updateTaskTimeout(c, taskid, nextRule.getData().getTimeSpan(), DateUtils.addMinutes(task.create_time, nextRule.getData().getTimeSpan()));
                    });
                } else {
                    DbHelper.call((c) -> {
                        new t_task_dal().updateTaskTimeout(c, taskid, 0, DateUtils.addMinutes(task.create_time, 0));
                    });
                }
            }
            //}
        }catch (Exception e){
            throw filterException(flowid,e);
        }
    }

    private void doTimeoutRule(t_task_model task,Model.TimeOutRule rule){
        if(rule.getTimeoutDealWayEnum() == Model.TimeoutDealWayEnum.Notify){
            sendMessage(task.id,Model.NotifyEnum.valueOf(rule.getValueScript()),task.flow_approve_userno, task.flow_title,ContextUtils.getBean(FlowProperties.class, false).getTimeoutNotifyTemplate());
        }else if(rule.getTimeoutDealWayEnum() == Model.TimeoutDealWayEnum.Script){
            CommonApi.doScript(task.flow_id,task.id,"",rule.getValueScript(), flowVariables);
            updateFlowVariable(task.flow_id,flowVariables.values());
        } else if(rule.getTimeoutDealWayEnum() == Model.TimeoutDealWayEnum.Transfer){
            transfer(task.id,rule.getValueScript(),"触发超时规则->系统自动转交");
        }
    }

    public void transfer(int taskid,String userno,String remark){
        int flowid =-1;
        try {
            val user = UserApi.getUser(userno);
            val task = getTaskModel(taskid);flowid = task.flow_id;
            if (task == null || task.getTask_state() != TaskStateEnum.waiting.Value) {
                throw new FlowException("任务:"+taskid+"不存在或已处理");
            }
            if (task.getFlow_approve_userno().equals(userno)) {
                throw new FlowException("不能把任务:"+taskid+"的审批用户转交给自己");
            }
            val model = new t_approve_log_model(0, task.flow_id, taskid, "转交->" + user.getName() + "(" + userno + ")", remark, getName(), task.flow_approve_userno, new Date(), task.flow_approve_by, new Date(), task.flow_approve_userno, task.flow_approve_by);
            addApproveLog(model);
            DbHelper.call((c) -> {
                new t_task_dal().transferTask(c, taskid, user.getNo(), user.getName());
            });
        }
        catch (Exception e){
            throw filterException(flowid,e);
        }
    }

    public void closeTask(int taskid,String userno,String remark) {
        int flowid =-1;
        try {
            val user = UserApi.getUser(userno);
            val task = getTaskModel(taskid);flowid = task.flow_id;
            if (task == null || task.getTask_state() == TaskStateEnum.close.Value) {
                throw new FlowException("任务:"+taskid+"已关闭或不存在");
            }
            val model = new t_approve_log_model(0, task.flow_id, taskid, "关闭任务", remark, getName(), task.flow_approve_userno, new Date(), task.flow_approve_by, new Date(), task.flow_approve_userno, task.flow_approve_by);
            addApproveLog(model);
            DbHelper.call((c) -> {
                new t_task_dal().updateTaskState(c, taskid, user.getNo(), TaskStateEnum.close.Value);
            });
            updateFlowApprove(flowid);
        }
        catch (Exception e){
            throw filterException(flowid,e);
        }
    }

    public void deleteTask(int taskid,String userno,String remark) {
        int flowid =-1;
        try {
            val user = UserApi.getUser(userno);
            val task = getTaskModel(taskid);flowid = task.flow_id;
            val model = new t_approve_log_model(0, task.flow_id, taskid, "删除任务", remark, getName(), task.flow_approve_userno, new Date(), task.flow_approve_by, new Date(), task.flow_approve_userno, task.flow_approve_by);
            addApproveLog(model);
            DbHelper.call((c) -> {
                new t_task_dal().delete(c, taskid);
            });
            updateFlowApprove(flowid);
        }
        catch (Exception e){
            throw filterException(flowid,e);
        }
    }

    public void doTask(int taskid,String userno,String approveResult,String remark,List<Variable> variables){
        int flowid = -1;
        try {
            val user2 = UserApi.getUser(userno);
            //获取并更新任务
            val task = getTaskModel(taskid);
            flowid = task.flow_id;
            if(task.getTask_state()!=TaskStateEnum.waiting.Value)
            {
                throw new FlowException("当前任务"+taskid+"已处理,请勿重复执行");
            }
            updateFlowVariable(flowid,variables);
            val flow = getFlowModel(task.flow_id);
            DbHelper.call((c)->{new t_task_dal().updateTaskResult(c,task.id,user2.getNo(),approveResult);});
            DbHelper.call((c)->{new t_task_dal().updateTaskState(c,task.id,user2.getNo(),TaskStateEnum.done.Value);});
            sendMessage(taskid,this.modelNode.getEndNotify(),flow.getCreate_userno(),  flow.title,ContextUtils.getBean(FlowProperties.class,false).getEndNotifyTemplate());
            val model2 = new t_approve_log_model(0,task.flow_id,taskid, StringUtils.nullToEmpty(approveResult),remark,getName(),user2.getNo(),new Date(),user2.getName(),new Date(),user2.getNo(),user2.getName());
            addApproveLog(model2);

            val results = DbHelper.get((c)->{return new t_task_dal().getResults(c,task.flow_id,task.flow_approve_node,task.batch_id);});
            if(this.modelNode.getApproveUser().getDistributeModeEnum() == Model.DistributeModeEnum.Once){
                route(task.flow_id,task.id,results,task.batch_id);
            }else if(this.modelNode.getApproveUser().getDistributeModeEnum() == Model.DistributeModeEnum.OneByOne){
                if(route(task.flow_id,task.id,results,task.batch_id)==false)
                {
                    //如果流程未结束,创建用户任务
                    if(!StringUtils.isEmpty(task.getNext_userno())){
                        val usernos = Lists.newArrayList(task.getNext_userno().split(","));
                        val user = UserApi.getUser(usernos.get(0)); usernos.remove(0);
                        var nextusernos = String.join(",", usernos);
                        //创建用户任务
                        t_task_model model= new t_task_model(0,task.flow_id,task.flow_title,this.modelNode.getTitle(),user.getNo(),user.getName(),nextusernos,(byte)TaskStateEnum.waiting.Value,
                                new Date(),"系统",new Date(),"系统",nextTimeout(0),nextTimeoutWithTime(new Date(),0),"",task.batch_id);
                        val taskid2 = DbHelper.get((c)->new t_task_dal().add2(c,model));
                        sendMessage(taskid2,this.modelNode.getStartNotify(),task.getFlow_approve_userno(), flow.title, ContextUtils.getBean(FlowProperties.class,false).getStartNotifyTemplate());
                    }else
                    {
                        throw new StateEngineException(task.flow_id,"当前任务未找到下一个顺序分发人员");
                    }
                }
            }
        }catch (Exception e){
            throw filterException(flowid,e);
        }

    }

    private List<String> getApprovalUserNos(List<String> usernos){
        int num = this.modelNode.getApproveUser().getApproveNum();
        num = (num<=-1?10000:num);
        if(this.modelNode.getApproveUser().getFilterModeEnum() == Model.FilterModeEnum.Random) {
            CommonApi.random(usernos);
        }
        val list = usernos.stream().distinct().limit(num).collect(Collectors.toList());
        return list;
    }

    private void parseNextTimeout(int current, Ref<Model.TimeOutRule> currentTimeOutRule, Ref<Model.TimeOutRule> nextTimeOutRule){
        if(this.modelNode.getTimeOutRules()!=null) {
            this.modelNode.getTimeOutRules().sort((c1, c2) -> c1.getTimeSpan() - c2.getTimeSpan());
            for (val timeout : this.modelNode.getTimeOutRules()) {
                if(timeout.getTimeSpan() == current){
                    currentTimeOutRule.setData(timeout);
                }
                if (timeout.getTimeSpan() > current) {
                    if(nextTimeOutRule!=null)
                    {nextTimeOutRule.setData(timeout);}
                    return;
                }
            }
        }
    }

    private int nextTimeout(int current){
        if(this.modelNode.getTimeOutRules()!=null) {
            this.modelNode.getTimeOutRules().sort((c1, c2) -> c1.getTimeSpan() - c2.getTimeSpan());
            for (val timeout : this.modelNode.getTimeOutRules()) {
                if (timeout.getTimeSpan() > current) {
                    return timeout.getTimeSpan();
                }
            }
        }
        return 0;
    }

    private Date nextTimeoutWithTime(Date date,int current){
        if(this.modelNode.getTimeOutRules()!=null) {
            this.modelNode.getTimeOutRules().sort((c1, c2) -> c1.getTimeSpan() - c2.getTimeSpan());
            for (val timeout : this.modelNode.getTimeOutRules()) {
                if (timeout.getTimeSpan() > current) {
                    return DateUtils.addMinutes(date,current);
                }
            }
        }
        return date;
    }

}
